﻿namespace Fiddler
{
    using System;
    using System.Collections.Specialized;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Security.Authentication;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Windows.Forms;

    public class ServerPipe : BasePipe
    {
        protected bool _bIsConnectedToGateway;
        private bool _bIsConnectedViaSOCKS;
        private int _iMarriedToPID;
        private bool _isAuthenticated;
        private PipeReusePolicy _reusePolicy;
        private string _ServerCertChain;
        protected string _sPoolKey;
        internal static int _timeoutReceiveInitial = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.receive.initial", -1);
        internal static int _timeoutReceiveReused = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.receive.reuse", -1);
        internal static int _timeoutSendInitial = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.send.initial", -1);
        internal static int _timeoutSendReused = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.send.reuse", -1);
        internal DateTime dtConnected;
        private static StringCollection slAcceptableBadCertificates;
        internal ulong ulLastPooled;

        internal ServerPipe(Socket oSocket, string sName, bool bConnectedToGateway, string sPoolingKey)
            : base(oSocket, sName)
        {
            this.dtConnected = DateTime.Now;
            this._bIsConnectedToGateway = bConnectedToGateway;
            this.sPoolKey = sPoolingKey;
        }

        private static X509Certificate _GetDefaultCertificate()
        {
            if (FiddlerApplication.oDefaultClientCertificate != null)
            {
                return FiddlerApplication.oDefaultClientCertificate;
            }
            X509Certificate certificate = null;
            if (System.IO.File.Exists(CONFIG.GetPath("DefaultClientCertificate")))
            {
                certificate = X509Certificate.CreateFromCertFile(CONFIG.GetPath("DefaultClientCertificate"));
                if ((certificate != null) && FiddlerApplication.Prefs.GetBoolPref("fiddler.network.https.cacheclientcert", true))
                {
                    FiddlerApplication.oDefaultClientCertificate = certificate;
                }
            }
            return certificate;
        }

        internal object _GetTransportContext()
        {
            if (base._httpsStream != null)
            {
                return base._httpsStream.TransportContext;
            }
            return null;
        }

        private X509Certificate AttachClientCertificate(Session oS, object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
        {
            if (localCertificates.Count > 0)
            {
                this.MarkAsAuthenticated(oS.LocalProcessID);
                oS.oFlags["x-client-cert"] = localCertificates[0].Subject + " Serial#" + localCertificates[0].GetSerialNumberString();
                return localCertificates[0];
            }
            if ((remoteCertificate != null) || (acceptableIssuers.Length >= 1))
            {
                X509Certificate certificate = _GetDefaultCertificate();
                if (certificate != null)
                {
                    this.MarkAsAuthenticated(oS.LocalProcessID);
                    oS.oFlags["x-client-cert"] = certificate.Subject + " Serial#" + certificate.GetSerialNumberString();
                    return certificate;
                }
                if (CONFIG.bShowDefaultClientCertificateNeededPrompt && FiddlerApplication.Prefs.GetBoolPref("fiddler.network.https.clientcertificate.ephemeral.prompt-for-missing", true))
                {
                    FiddlerApplication.DoNotifyUser("The server [" + targetHost + "] requests a client certificate.\nPlease save a client certificate using the filename:\n\n" + CONFIG.GetPath("DefaultClientCertificate"), "Client Certificate Requested");
                    FiddlerApplication.Prefs.SetBoolPref("fiddler.network.https.clientcertificate.ephemeral.prompt-for-missing", false);
                }
                FiddlerApplication.Log.LogFormat("The server [{0}] requested a client certificate, but no client certificate was available.", new object[] { targetHost });
            }
            return null;
        }

        private static bool ConfirmServerCertificate(Session oS, string sExpectedCN, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            CertificateValidity oValidity = CertificateValidity.Default;
            FiddlerApplication.CheckOverrideCertificatePolicy(oS, sExpectedCN, certificate, chain, sslPolicyErrors, ref oValidity);
            switch (oValidity)
            {
                case CertificateValidity.ForceInvalid:
                    return false;

                case CertificateValidity.ForceValid:
                    return true;
            }
            if (((oValidity != CertificateValidity.ConfirmWithUser) && ((sslPolicyErrors == SslPolicyErrors.None) || CONFIG.IgnoreServerCertErrors)) || oS.oFlags.ContainsKey("X-IgnoreCertErrors"))
            {
                return true;
            }
            if ((sslPolicyErrors == SslPolicyErrors.RemoteCertificateNameMismatch) && oS.oFlags.ContainsKey("X-IgnoreCertCNMismatch"))
            {
                return true;
            }
            if ((slAcceptableBadCertificates != null) && slAcceptableBadCertificates.Contains(sExpectedCN + certificate.GetSerialNumberString()))
            {
                return true;
            }
            if (CONFIG.QuietMode)
            {
                return false;
            }
            string sTitle = (sslPolicyErrors == SslPolicyErrors.None) ? "Accept Remote Certificate" : "Ignore remote certificate error?";
            string str2 = (sslPolicyErrors == SslPolicyErrors.None) ? "the following certificate: " : ("a certificate that did not validate, due to " + sslPolicyErrors.ToString() + ".");
            string str3 = (sslPolicyErrors == SslPolicyErrors.None) ? string.Empty : "(This warning can be disabled by clicking Tools | Fiddler Options.)";
            string sHint = (sslPolicyErrors == SslPolicyErrors.None) ? "Accept this certificate?" : "Ignore errors and proceed anyway?";
            frmAlert alert = new frmAlert(sTitle, string.Format("Session #{0}: The remote server ({1}) presented {2}\r\n\r\nSUBJECT: {3}\r\nISSUER: {4}\r\nEXPIRES: {5}\r\n\r\n{6}", new object[] { oS.id, sExpectedCN, str2, certificate.Subject, certificate.Issuer, certificate.GetExpirationDateString(), str3 }), sHint, MessageBoxButtons.YesNo, MessageBoxDefaultButton.Button2);
            alert.TopMost = true;
            alert.StartPosition = FormStartPosition.CenterScreen;
            DialogResult result = (DialogResult)FiddlerApplication._frmMain.Invoke(new getDecisionDelegate(FiddlerApplication._frmMain.GetDecision), new object[] { alert });
            if (DialogResult.Yes == result)
            {
                if (slAcceptableBadCertificates == null)
                {
                    slAcceptableBadCertificates = new StringCollection();
                }
                slAcceptableBadCertificates.Add(sExpectedCN + certificate.GetSerialNumberString());
            }
            return (DialogResult.Yes == result);
        }

        public string DescribeConnectionSecurity()
        {
            if (base._httpsStream == null)
            {
                return "No connection security";
            }
            string str = string.Empty;
            if (base._httpsStream.IsMutuallyAuthenticated)
            {
                str = "== Client Certificate ==========\nUnknown.\n";
            }
            if (base._httpsStream.LocalCertificate != null)
            {
                str = "\n== Client Certificate ==========\n" + base._httpsStream.LocalCertificate.ToString(true) + "\n";
            }
            StringBuilder builder = new StringBuilder(0x800);
            builder.AppendFormat("Secure Protocol: {0}\n", base._httpsStream.SslProtocol.ToString());
            builder.AppendFormat("Cipher: {0} {1}bits\n", base._httpsStream.CipherAlgorithm.ToString(), base._httpsStream.CipherStrength);
            builder.AppendFormat("Hash Algorithm: {0} {1}bits\n", base._httpsStream.HashAlgorithm.ToString(), base._httpsStream.HashStrength);
            builder.AppendFormat("Key Exchange: {0} {1}bits\n", base._httpsStream.KeyExchangeAlgorithm.ToString(), base._httpsStream.KeyExchangeStrength);
            builder.Append(str);
            builder.AppendLine("\n== Server Certificate ==========");
            builder.AppendLine(base._httpsStream.RemoteCertificate.ToString(true));
            if (FiddlerApplication.Prefs.GetBoolPref("fiddler.network.https.storeservercertchain", false))
            {
                builder.AppendFormat("[Chain]\n {0}\n", this.GetServerCertChain());
            }
            return builder.ToString();
        }

        private static X509CertificateCollection GetCertificateCollectionFromFile(string sClientCertificateFilename)
        {
            X509CertificateCollection certificates = null;
            if (!string.IsNullOrEmpty(sClientCertificateFilename))
            {
                sClientCertificateFilename = Utilities.EnsurePathIsAbsolute(CONFIG.GetPath("Root"), sClientCertificateFilename);
                if (System.IO.File.Exists(sClientCertificateFilename))
                {
                    certificates = new X509CertificateCollection();
                    certificates.Add(X509Certificate.CreateFromCertFile(sClientCertificateFilename));
                    return certificates;
                }
                FiddlerApplication.Log.LogFormat("!! ERROR: Specified client certificate file '{0}' does not exist.", new object[] { sClientCertificateFilename });
            }
            return certificates;
        }

        internal string GetServerCertChain()
        {
            if (this._ServerCertChain != null)
            {
                return this._ServerCertChain;
            }
            if (base._httpsStream != null)
            {
                try
                {
                    X509Certificate2 certificate = new X509Certificate2(base._httpsStream.RemoteCertificate);
                    if (certificate == null)
                    {
                        return string.Empty;
                    }
                    StringBuilder builder = new StringBuilder();
                    X509Chain chain = new X509Chain();
                    chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
                    chain.Build(certificate);
                    for (int i = chain.ChainElements.Count - 1; i >= 1; i--)
                    {
                        builder.Append(SummarizeCert(chain.ChainElements[i].Certificate));
                        builder.Append(" > ");
                    }
                    if (chain.ChainElements.Count > 0)
                    {
                        builder.AppendFormat("{0} [{1}]", SummarizeCert(chain.ChainElements[0].Certificate), chain.ChainElements[0].Certificate.SerialNumber);
                    }
                    this._ServerCertChain = builder.ToString();
                    return builder.ToString();
                }
                catch (Exception exception)
                {
                    return exception.Message;
                }
            }
            return string.Empty;
        }

        internal void MarkAsAuthenticated(int clientPID)
        {
            this._isAuthenticated = true;
            int num = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.auth.reusemode", 0);
            if ((num == 0) && (clientPID == 0))
            {
                num = 1;
            }
            if (num == 0)
            {
                this.ReusePolicy = PipeReusePolicy.MarriedToClientProcess;
                this._iMarriedToPID = clientPID;
                this.sPoolKey = string.Format("PID{0}*{1}", clientPID, this.sPoolKey);
            }
            else if (num == 1)
            {
                this.ReusePolicy = PipeReusePolicy.MarriedToClientPipe;
            }
        }

        internal bool SecureExistingConnection(Session oS, string sCertCN, string sClientCertificateFilename, string sPoolingKey, ref int iHandshakeTime)
        {
            RemoteCertificateValidationCallback userCertificateValidationCallback = null;
            LocalCertificateSelectionCallback userCertificateSelectionCallback = null;
            Stopwatch stopwatch = Stopwatch.StartNew();
            try
            {
                this.sPoolKey = sPoolingKey;
                X509CertificateCollection certificateCollectionFromFile = GetCertificateCollectionFromFile(sClientCertificateFilename);
                if (userCertificateValidationCallback == null)
                {
                    userCertificateValidationCallback = delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
                    {
                        return ConfirmServerCertificate(oS, sCertCN, certificate, chain, sslPolicyErrors);
                    };
                }
                if (userCertificateSelectionCallback == null)
                {
                    userCertificateSelectionCallback = delegate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
                    {
                        return this.AttachClientCertificate(oS, sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers);
                    };
                }
                base._httpsStream = new SslStream(new NetworkStream(base._baseSocket, false), false, userCertificateValidationCallback, userCertificateSelectionCallback);
                SslProtocols oAcceptedServerHTTPSProtocols = CONFIG.oAcceptedServerHTTPSProtocols;
                if (oS.oFlags.ContainsKey("x-OverrideSslProtocols"))
                {
                    oAcceptedServerHTTPSProtocols = Utilities.ParseSSLProtocolString(oS.oFlags["x-OverrideSslProtocols"]);
                }
                base._httpsStream.AuthenticateAsClient(sCertCN, certificateCollectionFromFile, oAcceptedServerHTTPSProtocols, FiddlerApplication.Prefs.GetBoolPref("fiddler.network.https.checkcertificaterevocation", false));
                iHandshakeTime = (int)stopwatch.ElapsedMilliseconds;
            }
            catch (Exception exception)
            {
                iHandshakeTime = (int)stopwatch.ElapsedMilliseconds;
                FiddlerApplication.DebugSpew(exception.StackTrace + "\n" + exception.Message);
                FiddlerApplication.Log.LogFormat("fiddler.network.https> Failed to secure existing connection for {0}. {1}{2}", new object[] { sCertCN, exception.Message, (exception.InnerException != null) ? (" InnerException: " + exception.InnerException.ToString()) : "." });
                return false;
            }
            return true;
        }

        internal void setTimeouts()
        {
            try
            {
                int num = (base.iUseCount < 2) ? _timeoutReceiveInitial : _timeoutReceiveReused;
                int num2 = (base.iUseCount < 2) ? _timeoutSendInitial : _timeoutSendReused;
                if (num > 0)
                {
                    base._baseSocket.ReceiveTimeout = num;
                }
                if (num2 > 0)
                {
                    base._baseSocket.SendTimeout = num2;
                }
            }
            catch
            {
            }
        }

        private static string SummarizeCert(X509Certificate2 oCert)
        {
            if (!string.IsNullOrEmpty(oCert.FriendlyName))
            {
                return oCert.FriendlyName;
            }
            string subject = oCert.Subject;
            if (string.IsNullOrEmpty(subject))
            {
                return string.Empty;
            }
            if (subject.Contains("CN="))
            {
                return Utilities.TrimAfter(Utilities.TrimBefore(subject, "CN="), ",");
            }
            if (subject.Contains("O="))
            {
                return Utilities.TrimAfter(Utilities.TrimBefore(subject, "O="), ",");
            }
            return subject;
        }

        public override string ToString()
        {
            return string.Format("{0}[Key: {1}; UseCnt: {2} [{3}]; {4}; {5} (:{6} to {7}:{8} {9}) {10}]", new object[] { base._sPipeName, this._sPoolKey, base.iUseCount, base._sHackSessionList, base.bIsSecured ? "Secure" : "PlainText", this._isAuthenticated ? "Authenticated" : "Anonymous", base.LocalPort, base.Address, base.Port, this.isConnectedToGateway ? "Gateway" : "Direct", this._reusePolicy });
        }

        internal bool isAuthenticated
        {
            get
            {
                return this._isAuthenticated;
            }
        }

        internal bool isClientCertAttached
        {
            get
            {
                return ((base._httpsStream != null) && base._httpsStream.IsMutuallyAuthenticated);
            }
        }

        public bool isConnectedToGateway
        {
            get
            {
                return this._bIsConnectedToGateway;
            }
        }

        public bool isConnectedViaSOCKS
        {
            get
            {
                return this._bIsConnectedViaSOCKS;
            }
            set
            {
                this._bIsConnectedViaSOCKS = value;
            }
        }

        public IPEndPoint RemoteEndPoint
        {
            get
            {
                if (base._baseSocket == null)
                {
                    return null;
                }
                return (base._baseSocket.RemoteEndPoint as IPEndPoint);
            }
        }

        public PipeReusePolicy ReusePolicy
        {
            get
            {
                return this._reusePolicy;
            }
            set
            {
                this._reusePolicy = value;
            }
        }

        public string sPoolKey
        {
            get
            {
                return this._sPoolKey;
            }
            private set
            {
                if (CONFIG.bDebugSpew && !string.IsNullOrEmpty(this._sPoolKey))
                {
                    FiddlerApplication.Log.LogFormat("fiddler.pipes>{0} pooling key changing from '{1}' to '{2}'", new object[] { base._sPipeName, this._sPoolKey, value });
                }
                this._sPoolKey = value;
            }
        }
    }
}

